#include <boost/bind.hpp>
#include "TCPConnection.h"
#include "Config.h"
#include "Protocol.h"

TCPConnection::pointer TCPConnection::create(boost::asio::io_service& ios)
{
  return TCPConnection::pointer(new TCPConnection(ios));
}

boost::asio::ip::tcp::socket& TCPConnection::socket()
{
  return this->_socket;
}

TCPConnection::TCPConnection(boost::asio::io_service& io_service)
  : _socket(io_service),
    _timer(io_service, boost::posix_time::seconds(ALLOW_TIME_AWAY_CLT))
{
  this->setReadMod(readingHeader, sizeof(CSProtocol::Header));
  this->_readingData = NULL;
  this->_readingHeader = NULL;
  this->_client = NULL;
  this->initActions();
  this->_isAuthenticate = false;
}

void TCPConnection::start(TCPServer *server)
{
  this->_server = server;
  CSProtocol::Welcome welcome = CSProtocol::Welcome();
  welcome.sendTreat(*this);
  this->read();
}

void TCPConnection::write(CSProtocol::Header &header, CSProtocol::IData &data)
{
  time_t t;
  time(&t);
  std::cout << "Envoi de la commande => " << header.code << std::endl;
  if (this->_readingHeader && !header.spontaneous)
    header.id = this->_readingHeader->id;
  header.timestamp = t;
  boost::asio::async_write(this->_socket, boost::asio::buffer((char*)&header, sizeof(header)),
			   boost::bind(&TCPConnection::handleWrite,
				       shared_from_this(),
				       boost::asio::placeholders::error)
			   );
  if (header.dataLength > 0)
    {
      boost::asio::async_write(this->_socket, boost::asio::buffer((char*)&data, header.dataLength),
			       boost::bind(&TCPConnection::handleWrite,
					   shared_from_this(),
					   boost::asio::placeholders::error)
			       );
    }
}

void TCPConnection::read()
{
  std::cout << "sizeread=" << this->_curReadSize << std::endl;
  this->_socket.async_read_some(boost::asio::buffer(this->_bufferIn, this->_curReadSize),
				boost::bind(&TCPConnection::handleRead, shared_from_this(),
					    boost::asio::placeholders::error, this->_curReadSize)
				);
}

void TCPConnection::initActions()
{
  this->_actions[CSProtocol::Register::code] = &TCPConnection::createRegister;
  this->_actions[CSProtocol::Login::code] = &TCPConnection::createLogin;
  this->_actions[CSProtocol::Logout::code] = &TCPConnection::createLogout;
  this->_actions[CSProtocol::AddContact::code] = &TCPConnection::createAddContact;
  this->_actions[CSProtocol::Profil::code] = &TCPConnection::createProfil;
}

void TCPConnection::setReadMod(eReadMod newMod, int size)
{
  this->_curReadingMod = newMod;
  this->_curReadSize = size;
}

void TCPConnection::handleRead(const boost::system::error_code& error,
			       std::size_t bytes_transferred)
{
  if (!error)
    {
      if (this->_curReadingMod == readingHeader)
	{
	  delete this->_readingHeader;
	  this->_readingHeader = new CSProtocol::Header();
	  memcpy(this->_readingHeader, (CSProtocol::Header*)&this->_bufferIn, sizeof(CSProtocol::Header));
	}
      else
	this->_readingData = (CSProtocol::IData*)&this->_bufferIn;
      if (this->_curReadingMod == readingData)
	{
	  int code = this->_readingHeader->code;
	  std::cout << code << " id = > " << this->_readingHeader->id << std::endl;
	  if (this->_actions.count(code))
	    {
	      CSProtocol::IData *command = (this->*this->_actions[code])();
	      std::cout << "jepasse" << std::endl;
	      command->receiveTreat(*this, this->_readingData);
	      delete command;
	    }
	  this->setReadMod(readingHeader, sizeof(CSProtocol::Header));
	}
      else
	this->setReadMod(readingData, this->_readingHeader->dataLength);
      this->read();
    }
  else
    {
      std::cerr << "[Error] on read " << error.message() << std::endl;
      this->close();
    }
}

void TCPConnection::handleWrite(const boost::system::error_code& error)
{
  if (error)
    std::cerr << "[Error] on write : " << error.message() << std::endl;
}

void TCPConnection::close()
{
  this->_socket.close();
  std::cout << "[Server] Client disconnected." << std::endl;
}
